﻿#7 面向对象高级编程

#1.)给实例和类绑定方法
class Student(object):
	pass;

def printScore(self,score):
	print("score=",score)
	
std1 = Student()
from types import MethodType

std1.printScore = MethodType(printScore,std1) #给实例绑定方法,绑定的方法第一个参数也必须有self

std1.printScore(100)

std2 = Student() 

# std2.printScore(100) #会报错,因为上面只针对实例绑定

#给类绑定
Student.printScore =  MethodType(printScore,Student)

std2.printScore(90) #可以正常调用了

#2.)使用__slots__限制可以动态绑定的属性名

class Animal(object):
	#该语法限制了可以动态绑定的属性,在set内的属性名才能被绑定但是子类不受父类的限制
	__slots__ = ('typeName','run'); 

class Dog(Animal):
	__slots__ = ('age'); #子类如果添加了,则子类的限制也包括父类的,不添加则不受父类的限制

myAnim1 = Animal()
#myAnim1.age = 1 #会报错
	
myDog1 = Dog()

myDog1.age = 2

print("myDog1.age=",myDog1.age)
#3.)@property装饰器,python的setter和getter

class People(object):
	
	@property #相当于getter
	def name(self):
		if not hasattr(self,"_name"):
			self._name = "";
		return self._name
	
	@name.setter #setter
	def name(self,val):
		if not isinstance(val,str):
			raise  ValueError('name must be a str!')
		self._name = val
		
	@property #定义只读属性,只有getter
	def brith(self):
		return 2015-1990

p1 = People()
print("p1.name =",p1.name)

#4.)多重继承,多重继承一般设计是定义好主线继承关系的几个类,然后再额外定义一些类用多继承实现功能

#主线继承关系,动物->各种动物
#额外功能分为会走的和会飞的,不参与主线继承关系,通过多继承实现功能

class Animal(object):
	pass;
	
class Mammal(Animal): #哺乳动物
	pass;

class Bird(Animal): #鸟类
	pass;

#不能一直继承下来，这样会无休止继承所以另外定义，需要功能的类再继承	

#多重继承实现额外功能的类通常以MixIn结尾
class FlyAbleMixIn(object):
	def fly(self):
		print("I am flying! ")
	
class RunAbleMixIn(object):
	def fly(self):
		print("I am running! ")

#鹤
class Crane(Bird,FlyAbleMixIn): #多重继承,可以同时继承多个
	pass;
	
#兔子
class Rabbit(Mammal,RunAbleMixIn): #多重继承,
	pass;

#5.)定制类,主要讲解一些内部的方法 
#例如:__str__(), __repr__(),__iter__()可以自己实现迭代器功能,__getitem__()方法,如何自己实现一些定制功能

#__getattr__方法,该方法主要是在动态访问一些不存在属性时则会通过访问该函数返回(如果存在的属性不会访问该函数)
class Chain(object):
    def __init__(self, path=''):    	   	
        self._path = path

    def __getattr__(self, path):		
        return Chain('%s/%s' % (self._path, path)) #如果不存在则链式创建 _path 属性值

    def __str__(self):  #改函数定义了通过print(类名)输出的字符串;例如:prin(Chain)
        return self._path 
	
    __repr__ = __str__ # __repr__ 在命令行中打类名回车后输出的字符串,也就是作用于repr(obj)这样输出的字符串

print(Chain().status.user.timeline.list) # /status/user/timeline/list  为什么会输出这个？
print('Chain().status',Chain().status)

ch = Chain()
print('ch._path=',ch.a)
print(ch)



#__call__方法,通过该方法可以把实例本身变成函数调用
'''
__setattr__的用法,当类内定义了该函数那么该函数内赋值必须采用:
self.__dict__['name'] = name 这种形式赋值 因为self.name这种会被调用__setattr__函数代替这样就构成死循环
'''
#通过callable()函数，我们就可以判断一个对象是否是“可调用”对象。

def fn():
	print("I am a function")
	
class People(object):
	def __init__(self,name):		
		self.name = name
		self.age = 10;			
	def __call__(self):
		print("my name is %s" % self.name);
	
	def __getattr__(self,attr):
		if attr=="age":
			return 0;
	
	def __setattr__(self,attr,val):
		if attr=="age" and not isinstance(val,int):
			raise ValueError("年龄必须是整数")				
		self.__dict__[attr] = val #必须用这种方式访问和设置属性

p = People('hui');
p() #实例也是函数,因为People内部定义了__call__函数,实例执行的函数就是__call__函数执行的内容
print("p.age=",p.age)
p.age = 20
print("p.age=",p.age)
print(callable(fn))
print(callable(p))

#6.)使用枚举类
from enum import Enum,unique

#定义枚举

Month = Enum("Month",('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))

#打印出枚举值
for name,member in Month.__members__.items():
	pass
	#print("%s=>%s=>%s"%(name,member,member.value))

#访问枚举类型和枚举值
print(Month.Jan) #输出是字符串,枚举类型
print(Month.Jan.value) #枚举值

if 1==Month.Jan.value:
	print("YES")
else:
	print("NO")
	
#7.)使用元类

class Hello(object):
	def say(self):
		print("say hello to everyone!");
	def __str__(self):
		return 'Hello object ';
	__repr__=__str__

h = Hello();
h.say()
print(h)
print(type(Hello))
print(type(h))
#通过type函数创建类
'''
要创建一个class对象，type()函数依次传入3个参数：

class的名称；
继承的父类集合，注意Python支持多重继承，如果只有一个父类，别忘了tuple的单元素写法；
class的方法名称与函数绑定，这里我们把函数fn绑定到方法名hello上。

'''

def debug(self):
	print("I am a coder,debugging!");	

Coder = type("Coder",(object,),dict(debug=debug))
#用type函数定义个类
print(Coder)

c = Coder()
c.debug()

#8.)metaclass,(需要继续看教程，现在一头雾水[2016年3月14日17:26:46])
'''
metaclass是Python面向对象里最难理解，也是最难使用的魔术代码。正常情况下，你不会碰到需要使用metaclass的情况，
所以，以下内容看不懂也没关系，因为基本上你不会用到。
'''

